﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Serialization;
using NUnit.Framework;

namespace Sripirom.Pattern.UnitTest.Core.UnitTest
{
    public class CustomTestCaseSourceAttribute : TestCaseSourceAttribute
    {
        private static UnitTestClassConfigElement _testClassData;
        private static String _categoryName;
        private static String _classId;
        private static String _functionId;

        public CustomTestCaseSourceAttribute(String categoryName, String classId, String functionId)
            : base(typeof(TestCaseSourceFetcher), "GetData")
        {
            _categoryName = categoryName;
            _classId = classId;
            _functionId = functionId;
            _testClassData = UnitTestRepository<ConfigDataConfigurationSection>.Load.DefaultCatetory.TestClasses[_classId];
       
        }

        public static String CategoryName { get { return _categoryName; } }
        public static UnitTestClassConfigElement TestClassData { get { return _testClassData; } }
        public static String TestFunction { get { return _functionId; } }

        public class TestCaseSourceFetcher
        {

            public IEnumerable GetData
            {
                get
                {
                
                    foreach (UnitTestCaseConfigElement testCase in TestClassData.TestFunctions[CustomTestCaseSourceAttribute.TestFunction].TestCases)
                    {
                        IList<Object> parameters = new List<Object>();

                        foreach (UnitTestParameterConfigElement item in testCase.TestParameters)
                        {
                            parameters.Add(ConvertTo(item.Type, item.Value));
                        }
                        // AddReturn Value
                        parameters.Add(ConvertTo(testCase.ReturnType, testCase.ReturnValue));
                        parameters.Add(String.IsNullOrEmpty(testCase.Ignore)
                                        ? new String[] { }
                                        : testCase.Ignore.Split(','));
                        yield return new TestCaseData(parameters.ToArray()).SetCategory(CategoryName)
                                                                            .SetName(testCase.Name)
                                                                            .SetDescription(testCase.Description);
                    }
                }
            }                                     

            public object ConvertTo(string typeName, string value)
            {
                Type type = Type.GetType(typeName);
                /// <summary>
                /// Determines if a type is numeric.  Nullable numeric types are considered numeric.
                /// </summary>                            
                /// <remarks>
                /// Boolean is not considered numeric.
                /// </remarks>

                if (type == null)
                {
                    throw new ArgumentOutOfRangeException("Error Data");
                }

                switch (Type.GetTypeCode(type))
                {
                    case TypeCode.Decimal:
                        return Convert.ToDecimal(value);
                    case TypeCode.Double:
                        return Convert.ToDouble(value);
                    case TypeCode.Int16:
                        return Convert.ToInt16(value);
                    case TypeCode.Int32:
                        return Convert.ToInt32(value);
                    case TypeCode.Int64:
                        return Convert.ToInt64(value);
                    case TypeCode.SByte:
                        return Convert.ToSByte(value);
                    case TypeCode.Single:
                        return Convert.ToSingle(value);
                    case TypeCode.UInt16:
                        return Convert.ToUInt16(value);
                    case TypeCode.UInt32:
                        return Convert.ToUInt32(value);
                    case TypeCode.UInt64:
                        return Convert.ToInt64(value);
                    case TypeCode.Object:
                        //if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                        //{
                        //    return IsNumericType(Nullable.GetUnderlyingType(type));
                        //}
                        string decodeXml = HttpUtility.HtmlDecode(value);
                        try
                        {
                            var stringReader = new System.IO.StringReader(decodeXml);
                            var serializer = new XmlSerializer(type);
                            var obj = serializer.Deserialize(stringReader);
                            return obj;
                        }
                        catch
                        {
                            throw;
                        }



                    default: throw new ArgumentOutOfRangeException("Error Data");

                }


            }

        }

    }

}
